﻿#include "PickedDataProvider.h"

// VTK
#include <vtkPlanes.h>
#include <vtkActor.h>
#include <vtkActor2D.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITK_Component/FITKAcousticsVTKGraphAdaptor/FITKAcousticsVTKGraphObject3D.h"

// APP
#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKKeyMouseStates.h"

// Pick
#include "GUIPickInfo.h"
#include "PickedData.h"
#include "PickedDataCalculator.h"

namespace GraphData
{
    // 静态变量初始化。
    PickedDataProvider* PickedDataProvider::s_instance{ nullptr };

    PickedDataProvider* PickedDataProvider::getInstance()
    {
        // 获取实例。
        if (!s_instance)
        {
            s_instance = new PickedDataProvider;
        }

        return s_instance;
    }

    void PickedDataProvider::Delete()
    {
        if (s_instance)
        {
            delete s_instance;
        }

        s_instance = nullptr;
    }

    PickedDataProvider::PickedDataProvider()
    {
        // 初始化键盘监视器。
        m_keyStates = FITKGLODATA->getKeyMouseStates();
    }

    PickedDataProvider::~PickedDataProvider()
    {
        // 清除拾取数据。
        clearPickedData();
    }

    void PickedDataProvider::clearPickedData()
    {
        // 清除并析构。
        for (PickedData* data : m_pickedDataList)
        {
            if (data)
            {
                // 清除拾取高亮。
                data->clearHighlight();
                delete data;
            }
        }

        m_pickedDataList.clear();
    }

    PickedData* PickedDataProvider::getPickedData(vtkActor* actor, int index, double* pickedWorldPos)
    {
        // 获取拾取方式及附加信息。
        GUI::GUIPickInfoStru pickInfo = GUI::GUIPickInfo::GetPickInfo();

        // 创建拾取信息。
        PickedData* pickedData = new PickedData(pickInfo, actor, index, pickedWorldPos);

        // 判断拾取是否有效。
        if (!pickedData->isValid())
        {
            delete pickedData;
            return nullptr;
        }

        // 创建临时计算器。
        PickedDataCalculator calculator(pickedData);
        calculator.calculate();

        return pickedData;
    }

    PickedData* PickedDataProvider::getPickedData(vtkActor2D* actor)
    {
        // 获取拾取方式及附加信息。
        GUI::GUIPickInfoStru pickInfo = GUI::GUIPickInfo::GetPickInfo();

        // 创建拾取信息。
        PickedData* pickedData = new PickedData(pickInfo, actor);

        // 判断拾取是否有效。
        if (!pickedData->isValid())
        {
            delete pickedData;
            return nullptr;
        }

        // 创建临时计算器。
        PickedDataCalculator calculator(pickedData);
        calculator.calculate();

        return pickedData;
    }

    PickedData* PickedDataProvider::getPickedData(vtkImageActor* actor)
    {
        // 获取拾取方式及附加信息。
        GUI::GUIPickInfoStru pickInfo = GUI::GUIPickInfo::GetPickInfo();

        // 创建拾取信息。
        PickedData* pickedData = new PickedData(pickInfo, actor);

        // 判断拾取是否有效。
        if (!pickedData->isValid())
        {
            delete pickedData;
            return nullptr;
        }

        // 创建临时计算器。
        PickedDataCalculator calculator(pickedData);
        calculator.calculate();

        return pickedData;
    }

    void PickedDataProvider::addPicked(vtkActor* actor, int index, double* pickedWorldPos)
    {
        // 计算生成拾取数据。
        PickedData* pickedData = getPickedData(actor, index, pickedWorldPos);
        if (!pickedData)
        {
            return;
        }

        // 清空数据。
        clearDataIfNeccessary();

        // 处理拾取数据。
        dealPickedData(pickedData);

        // 高亮并发送数据拾取信号。
        checkPickData();
    }

    void PickedDataProvider::addPicked(vtkActor2D* actor)
    {
        PickedData* pickedData = getPickedData(actor);
        if (!pickedData)
        {
            return;
        }

        // 清空数据。
        clearDataIfNeccessary();

        // 处理拾取数据。
        dealPickedData(pickedData);

        // 高亮并发送数据拾取信号。
        checkPickData();
    }

    void PickedDataProvider::addPicked(vtkImageActor* actor)
    {
        PickedData* pickedData = getPickedData(actor);
        if (!pickedData)
        {
            return;
        }

        // 清空数据。
        clearDataIfNeccessary();

        // 处理拾取数据。
        dealPickedData(pickedData);

        // 高亮并发送数据拾取信号。
        checkPickData();
    }

    void PickedDataProvider::addPicked(QList<vtkActor*> actors, vtkPlanes* pickPlanes)
    {
        // 获取拾取方式。
        GUI::GUIPickInfoStru pickInfo = GUI::GUIPickInfo::GetPickInfo();

        // Single拾取模式禁止框选。
        if (pickInfo._pickMethod == GUI::GUIPickInfo::PickMethod::PMSingle)
        {
            return;
        }

        // 清空数据。
        clearDataIfNeccessary();

        for (vtkActor* actor : actors)
        {
            // 创建拾取信息。
            PickedData* pickedData = new PickedData(pickInfo, actor, pickPlanes);

            // 判断拾取是否有效。
            if (!pickedData->isValid())
            {
                delete pickedData;
                continue;
            }

            // 创建临时计算器。
            PickedDataCalculator calculator(pickedData);
            calculator.calculate();

            // 处理拾取数据。
            dealPickedData(pickedData);
        }

        // 高亮并发送数据拾取信号。
        checkPickData();
    }

    void PickedDataProvider::dealPickedData(PickedData* data)
    {
        if (!data)
        {
            return;
        }

        GUI::GUIPickInfoStru pickInfo = GUI::GUIPickInfo::GetPickInfo();

        // 保存拾取时按键状态。
        bool ctrlPressed = m_keyStates->keyPressed(Qt::Key::Key_Control);
        bool shiftPressed = m_keyStates->keyPressed(Qt::Key::Key_Shift);
        data->saveKeyFlags(Qt::Key::Key_Control, ctrlPressed);
        data->saveKeyFlags(Qt::Key::Key_Shift, shiftPressed);

        // 获取拾取对象类型。
        PickedDataType pdType = data->getPickedDataType();
        switch (pdType)
        {
        case GraphData::GeoVertPick:
        case GraphData::GeoEdgePick:
        case GraphData::GeoFacePick:
        case GraphData::GeoSolidPick:
        case GraphData::MeshCellPick:
        {
            // 获取相同模型拾取信息进行合并，否则直接添加。
            PickedData* brotherData = getSameModelPickData(data);
            if (brotherData)
            {
                // 2024/11/05 修改默认增量模式为智能融合。
                brotherData->merge(data);
            }
            else
            {
                m_pickedDataList.push_back(data);
            }

            break;
        }
        case GraphData::DiscNodePick:
        {
            // 根据当前键盘按键判断数据处理逻辑。
            // Shift与Control均未按下且不为框选模式则清空拾取重新选择。
            if (!ctrlPressed && !shiftPressed && 
                pickInfo._pickMethod != GUI::GUIPickInfo::PickMethod::PMIndividually)
            {
                clearPickedData();
                m_pickedDataList.push_back(data);
            }
            // Shift或Ctrl按下或同时按下则进行增量拾取。
            else
            {
                // 获取相同模型拾取信息进行合并，否则直接添加。
                PickedData* brotherData = getSameModelPickData(data);
                if (brotherData)
                {
                    // 2024/11/05 修改默认增量模式为智能融合。
                    brotherData->merge(data);
                }
                else
                {
                    m_pickedDataList.push_back(data);
                }
            }

            break;
        }
        case GraphData::SEABeamPick:
        case GraphData::SEAShellPick:
        case GraphData::SEACavityPick:
        case GraphData::SEASemiInfFluidPick:
        case GraphData::JuncPointPick:
        case GraphData::JuncLinePick:
        case GraphData::JuncAreaPick:
        case GraphData::LoadPointForcePick:
        case GraphData::LoadPowerPick:
        case GraphData::LoadConstraintPick:
        case GraphData::LoadImportPick:
        case GraphData::LoadExcForMomPick:
        case GraphData::FEFacePick:
        case GraphData::FEStructureShellPick:
        case GraphData::FEStructureBeamPick:
        case GraphData::FEAcousticPick:
        default:
        {
            // 整个数据拾取则将相同对象直接移除。
            PickedData* brotherData = getSameModelPickData(data);
            if (brotherData)
            {
                m_pickedDataList.removeOne(brotherData);
                brotherData->clearHighlight();
                delete brotherData;
            }
            else
            {
                m_pickedDataList.push_back(data);
            }

            break;
        }
        }

        // 移除已经被清空的数据。
        for (int i = m_pickedDataList.count() - 1; i >= 0; i--)
        {
            PickedData* data = m_pickedDataList[i];
            if (data->isEmpty())
            {
                m_pickedDataList.removeAt(i);
                data->clearHighlight();
                delete data;
            }
        }
    }

    PickedData* PickedDataProvider::getSameModelPickData(PickedData* data)
    {
        if (!data || m_pickedDataList.isEmpty())
        {
            return nullptr;
        }

        // 寻找拾取到的同模型数据。
        for (PickedData* pData : m_pickedDataList)
        {
            if (data->isSameAs(pData))
            {
                return pData;
            }
        }

        return nullptr;
    }

    void PickedDataProvider::checkPickData()
    {
        // 高亮数据。
        for (PickedData* pData : m_pickedDataList)
        {
            if (!pData)
            {
                continue;
            }

            pData->highlight();
        }

        // 如果有数据被拾取，并且最后一次拾取数据为结束状态则发送信号。
        if (m_pickedDataList.count() != 0)
        {
            //有对象拾取
            emit sig_pickedData();
            PickedData* pData = m_pickedDataList.last();
            if (pData){
                //拾取结束
                if (pData->isFinished()){
                    emit sig_pickedOver();
                }
            }
        }
    }

    void PickedDataProvider::clearDataIfNeccessary()
    {
        // 获取拾取方式及附加信息。
        GUI::GUIPickInfoStru pickInfo = GUI::GUIPickInfo::GetPickInfo();

        // 单个拾取模式需要清除历史拾取数据。
        if (pickInfo._pickMethod == GUI::GUIPickInfo::PickMethod::PMSingle && 
            pickInfo._pickObjType != GUI::GUIPickInfo::PickObjType::POBJDiscNode)
        {
            // 清除拾取数据。
            clearPickedData();
        }
        else
        {
            // 是否需要合并数据。
            bool needAdd = m_keyStates->keyPressed(Qt::Key::Key_Control) ||
                m_keyStates->keyPressed(Qt::Key::Key_Shift);

            // 不需要操作数据说明是普通框选，需要清空历史拾取信息。
            if (!needAdd)
            {
                clearPickedData();
            }
        }
    }

    QList<PickedData*> PickedDataProvider::getPickedList()
    {
        return m_pickedDataList;
    }
}